package com.wj.dubbo.service.common;

import java.util.concurrent.TimeUnit;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;

import com.wj.dubbo.service.common.lock.IBillIdentify;

@Service
public class RedisBillLockHandler {  

    private static final Logger LOGGER = Logger.getLogger(RedisBillLockHandler.class);  

    private static final int DEFAULT_SINGLE_EXPIRE_TIME = 600;  

    @Autowired
    private RedisTemplate<String, String> redisTemplate; 
    
    private  ValueOperations<String, String> getValueOperations(){
    	return redisTemplate.opsForValue();
    }
    
    private boolean setNX(String key,String value){
		return getValueOperations().setIfAbsent(key, value);
	}
    private String getLock(String key){
		return getValueOperations().get(key);
	}
    
	private void delete(String key){
		redisTemplate.delete(key);
	}
	
	private void expire(String key, long timeout){
		redisTemplate.expire(key, timeout, TimeUnit.SECONDS);
	}

    /** 
     * 获取锁  如果锁可用   立即返回true，  否则返回false 
     * @param billIdentify 
     * @return 
     */  
    public boolean tryLock(IBillIdentify billIdentify) {
        return tryLock(billIdentify, 0L, null);  
    }  

    /** 
     * 锁在给定的等待时间内空闲，则获取锁成功 返回true， 否则返回false 
     * @param billIdentify 
     * @param timeout 
     * @param unit 
     * @return 
     */  
    public boolean tryLock(IBillIdentify billIdentify, long timeout, TimeUnit unit) {  
        String key = (String) billIdentify.uniqueIdentify();  
        try {  
            long nano = System.nanoTime();  
            do {  
                LOGGER.debug("try lock key: " + key);
                if (setNX(key, key)) {   
                	expire(key, DEFAULT_SINGLE_EXPIRE_TIME);  
                    LOGGER.debug("get lock, key: " + key + " , expire in " + DEFAULT_SINGLE_EXPIRE_TIME + " seconds.");  
                    return Boolean.TRUE;  
                } else { // 存在锁  
                    if (LOGGER.isDebugEnabled()) {  
                        String desc = getLock(key);  
                        LOGGER.debug("key: " + key + " locked by another business：" + desc);  
                    }  
                }  
                if (timeout == 0) {  
                    break;  
                }  
                Thread.sleep(30);  
            } while ((System.nanoTime() - nano) < unit.toNanos(timeout));  
            return Boolean.FALSE;  
        }catch (Exception e) {  
            LOGGER.error(e.getMessage(), e);  
        }  
        return Boolean.FALSE;  
    }  

    /** 
     * 如果锁空闲立即返回   获取失败 一直等待 
     * @param billIdentify 
     */  
    public void lock(IBillIdentify billIdentify) {  
        String key = (String) billIdentify.uniqueIdentify();  
        try {  
            do {  
                LOGGER.debug("lock key: " + key);
                if (setNX(key, key)) {   
                	expire(key, DEFAULT_SINGLE_EXPIRE_TIME);  
                    LOGGER.debug("get lock, key: " + key + " , expire in " + DEFAULT_SINGLE_EXPIRE_TIME + " seconds.");  
                    return;  
                } else {  
                    if (LOGGER.isDebugEnabled()) {  
                    	String desc = getLock(key);  
                        LOGGER.debug("key: " + key + " locked by another business：" + desc);  
                    }  
                }  
                Thread.sleep(30); 
            } while (true);  
        }catch (Exception e) {  
            LOGGER.error(e.getMessage(), e);  
        } 
    }  

    /** 
     * 释放锁 
     * @param identify 
     */  
    public void unLock(IBillIdentify identify) {  
    	String key = (String) identify.uniqueIdentify();
        try {
        	delete(key); 
            LOGGER.debug("release lock, keys :" + key);
        }catch (Exception e) {  
            LOGGER.error(e.getMessage(), e);  
        } 
    }  
     
}